////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
//  File name:   terrain_sector.h
//  Version:     v1.00
//  Created:     28/5/2001 by Vladimir Kajalin
//  Compilers:   Visual Studio.NET
//  Description: 
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#ifndef SECINFO_H
#define SECINFO_H

#define MML_NOT_SET ((uint8)-1)

#define ARR_TEX_OFFSETS_SIZE 4
#define MAX_TERRAIN_SURFACE_TYPES 128  // Was 16 in Crysis.
#define MAX_SURFACE_TYPES_COUNT 128    // Was 32 in Crysis. (Why not 16? Why separate from the above?)
static const int SECTOR_PALETTE_SIZE = 16;	// Local (per sector) surface type count limit, 16 as in Crysis.
static const int STYPE_BIT_MASK = 1|2|4|8;	// surface type id
static const int STYPE_HOLE = 127;					// This surface type is reserved for holes in the terrain
static const int STYPE_LOCAL_HOLE = 15;			// Palette entry reserved to be mapped to STYPE_HOLE

#include "BasicArea.h"
#include "Array2d.h"

#include <SpuUtils.h>
enum eTexureType
{
	ett_Diffuse,
	ett_LM
};

class CUpdateTerrainTempData;

struct SVoxSurfaceTypeInfo
{
  SVoxSurfaceTypeInfo() { nInUse=0; }
  byte nInUse;
};

struct SSurfaceTypeInfo
{
	SSurfaceTypeInfo() { memset(this, 0, sizeof(SSurfaceTypeInfo)); }
	struct SSurfaceType * pSurfaceType;
  IRenderMesh * arrpRM[3];

	bool HasRM() { return arrpRM[0] || arrpRM[1] || arrpRM[2]; }
	int GetIndexCount();
	void DeleteRenderMeshes(IRenderer * pRend);

	void GetMemoryUsage( ICrySizer *pSizer ) const {/*nothing*/}
};

struct SRangeInfo 
{
  SRangeInfo()
  {
    fOffset = 0;
    fRange = 0;
    nModified = false;
    pHMData = NULL;
  }

	void UpdateBitShift(int nUnitToSectorBS)
	{
		const int nSecSize = 1<<nUnitToSectorBS;
		int n = nSize-1;
		nUnitBitShift = 0;
		while(n>0 && nSecSize>n) 
		{ 
			nUnitBitShift++; 
			n*=2; 
		}
	}

	inline uint32 GetRawDataByIndex(unsigned i) const
	{
//		assert(i < nSize*nSize);
		assert(pHMData);

		return pHMData[i];
	}

	inline uint32 GetRawData(unsigned nX, unsigned nY) const
	{
		assert(nX < nSize);
		assert(nY < nSize);
		assert(pHMData);

		return  GetRawDataByIndex(nX*nSize + nY);
	}

	inline float RawDataToHeight(uint32 data) const
	{
		return 0.05f*iOffset + (data>>4)*iStep*0.05f;
	}


	inline float GetHeightByIndex(unsigned i) const
	{
//		assert(i < nSize*nSize);
		assert(pHMData);

		return RawDataToHeight(pHMData[i]);
	}

	inline float GetHeight(unsigned nX, unsigned nY) const
	{
		assert(nX < nSize);
		assert(nY < nSize);
		assert(pHMData);

		return  GetHeightByIndex(nX*nSize + nY);
	}

	inline uint32 GetSurfaceTypeByIndex(unsigned i) const
	{
//		assert(i < nSize*nSize);
		assert(pHMData);

		return pHMData[i] & STYPE_BIT_MASK;
	}

	inline uint32 GetSurfaceType(unsigned nX, unsigned nY) const
	{
		assert(nX < nSize);
		assert(nY < nSize);
		assert(pHMData);

		return  GetSurfaceTypeByIndex(nX*nSize + nY);
	}

  inline void SetDataLocal( int nX, int nY, uint16 usValue )
  {
    assert(nX>=0 && nX<(int)nSize);
    assert(nY>=0 && nY<(int)nSize);
    assert(pHMData);
    pHMData[nX*nSize + nY] = usValue;
  }

	inline uint16 GetDataUnits( int nX_units, int nY_units ) const
	{
		int nMask = nSize-2;
		int nX = nX_units >> nUnitBitShift;
		int nY = nY_units >> nUnitBitShift;
		return GetRawData(nX&nMask, nY&nMask);
	}

	// Finds or selects a 4-bit index in this sector (0-14) to represent the given global surface type index (0-126).
	uint16 GetLocalSurfaceTypeID(uint16 usGlobalSurfaceTypeID)
	{
		// Local 0x0F and global 0x7F are reserved for holes.
		if (usGlobalSurfaceTypeID == STYPE_HOLE) return STYPE_LOCAL_HOLE;
		if (pSTPalette)
		{
			// Check if a local entry has already been assigned for this global entry.
			for (uint16 i = 0; i < SECTOR_PALETTE_SIZE - 1; i++)
				if (pSTPalette[i] == usGlobalSurfaceTypeID)
					return i;
			// No local entry has been assigned; look for an entry that is marked as currently unused.
			for (uint16 i = 0; i < SECTOR_PALETTE_SIZE - 1; i++)
				if (pSTPalette[i] == STYPE_HOLE)
				{
					pSTPalette[i] = (uchar)usGlobalSurfaceTypeID;
					return i;
				}
				// No local entry is marked as unused; look for one whose local ID does not actually occur in the data.
				int nUsageCounters[SECTOR_PALETTE_SIZE];
				memset(nUsageCounters, 0, sizeof(nUsageCounters));
				int nCount = nSize * nSize;
				for (uint16 i = 0; i < nCount; i++) nUsageCounters[pHMData[i] & STYPE_BIT_MASK]++;
				for (uint16 i = 0; i < SECTOR_PALETTE_SIZE - 1; i++)
					if (!nUsageCounters[i])
					{
						pSTPalette[i] = (uchar)usGlobalSurfaceTypeID;
						return i;
					}
				// Could not assign a local ID; mark the problem area with holes. (Should not happen, we have integrity checks.)
				return STYPE_LOCAL_HOLE;
		}
		else
		{
			// If the sector still has no palette, create one and assign local ID 0.
			pSTPalette = new uchar[SECTOR_PALETTE_SIZE];
			for (int i = 0; i < SECTOR_PALETTE_SIZE; pSTPalette[i++] = STYPE_HOLE);
			pSTPalette[0] = (uchar)usGlobalSurfaceTypeID;
			return 0;
		}
	}

	float fOffset;
	float fRange;
	uint16 * pHMData;

  uint16 nSize;
	uint8 nUnitBitShift;
  uint8 nModified;
	uchar *pSTPalette; // Maps the local surface type indices from the HM to the global ones in CTerrain


	int iOffset;
	int iRange;
	int iStep;
};

template <class T> class TPool
{
public:

	TPool(int nPoolSize)
	{
    m_nPoolSize = nPoolSize;
		m_pPool = new T[nPoolSize];
		m_lstFree.PreAllocate(nPoolSize,0);
		m_lstUsed.PreAllocate(nPoolSize,0);
		for(int i=0; i<nPoolSize; i++)
			m_lstFree.Add(&m_pPool[i]);
	}

	~TPool()
	{
		delete [] m_pPool;
	}

	void ReleaseObject(T * pInst)
	{
		if(m_lstUsed.Delete(pInst))
			m_lstFree.Add(pInst);
	}

	int GetUsedInstancesCount(int & nAll)
	{
		nAll = m_nPoolSize;
		return m_lstUsed.Count();
	}

	T * GetObject() 
	{
		T * pInst = NULL;
		if(m_lstFree.Count())
		{
			pInst = m_lstFree.Last();
			m_lstFree.DeleteLast();
			m_lstUsed.Add(pInst);
		}
		else
			assert("TPool::GetObject: Out of free elements error");

		return pInst;
	}

	void GetMemoryUsage(class ICrySizer * pSizer) const
	{
    pSizer->AddObject(m_lstFree);
		pSizer->AddObject(m_lstUsed);

    if(m_pPool)
      for(int i=0; i<m_nPoolSize; i++)
	  	  m_pPool[i].GetMemoryUsage(pSizer);
	}

	PodArray<T*> m_lstFree;
	PodArray<T*> m_lstUsed;
	T * m_pPool;
  int m_nPoolSize;
};

#define MAX_PROC_OBJ_CHUNKS_NUM 128

struct SProcObjChunk : public Cry3DEngineBase
{
	CVegetation * m_pInstances;
	SProcObjChunk();
	~SProcObjChunk();
  void GetMemoryUsage(class ICrySizer * pSizer) const;
};

typedef TPool<SProcObjChunk> SProcObjChunkPool;

class CProcObjSector : public Cry3DEngineBase
{
public:
	CProcObjSector() { m_nProcVegetNum = 0; m_ProcVegetChunks.PreAllocate(32); }
	~CProcObjSector();
	CVegetation * AllocateProcObject();
	void ReleaseAllObjects();
	int GetUsedInstancesCount(int & nAll) { nAll = m_ProcVegetChunks.Count(); return m_nProcVegetNum; }
	void GetMemoryUsage(ICrySizer*pSizer) const;

protected:
	PodArray<SProcObjChunk*> m_ProcVegetChunks;
	int m_nProcVegetNum;
};

typedef TPool<CProcObjSector> CProcVegetPoolMan;


struct STerrainNodeLeafData
{
	STerrainNodeLeafData() { memset(this,0,sizeof(*this)); }
	~STerrainNodeLeafData();
	float m_arrTexGen[MAX_RECURSION_LEVELS][ARR_TEX_OFFSETS_SIZE];
	short m_arrpNonBorderIdxNum[MAX_SURFACE_TYPES_COUNT][4];
	PodArray<CTerrainNode*> m_lstNeighbourSectors;
	PodArray<int> m_lstNeighbourLods;
	IRenderMesh * m_pRenderMesh;
};

struct CTerrainNode : public Cry3DEngineBase, public IShadowCaster, public IStreamCallback
{
public:

	friend class CTerrain;
  friend class CTerrainUpdateDispatcher;

	virtual void Render(const SRendParams &RendParams);
  const AABB GetBBox() const;
  virtual const AABB GetBBoxVirtual() { return GetBBox(); }
  virtual struct ICharacterInstance* GetEntityCharacter( unsigned int nSlot, Matrix34A * pMatrix = NULL, bool bReturnOnlyVisible = false ) {return NULL;};
  virtual bool IsRenderNode() { return false; }
  virtual EERType GetRenderNodeType() { return eERType_NotRenderNode; }

	//////////////////////////////////////////////////////////////////////////
	// IStreamCallback
	//////////////////////////////////////////////////////////////////////////
	// streaming
	virtual void StreamOnComplete (IReadStream* pStream, unsigned nError);	
	//////////////////////////////////////////////////////////////////////////
	void StartSectorTexturesStreaming(bool bFinishNow);

  void Init(int x1, int y1, int nNodeSize, CTerrainNode * pParent, bool bBuildErrorsTable, int nSID);
  CTerrainNode() :
    m_nSID(0),
    m_nNodeTexSet(0,0),
    m_nTexSet(0,0),
    m_nNodeTextureOffset(-1),
    m_nNodeHMDataOffset(-1),
    m_pParent(),
    m_pLeafData(NULL),
    m_pUpdateTerrainTempData(NULL),
    m_pChilds(0),
    m_nLastTimeUsed(0),
    m_nSetLodFrameId(0),
    m_pGeomErrors(0),
    m_pProcObjPoolPtr(0),
    m_nGSMFrameId(0),
    m_pRNTmpData(0)
  {
    memset(&m_arrfDistance, 0, sizeof(m_arrfDistance));
    m_nNodesCounter++;
  }
  ~CTerrainNode();
	static void ResetStaticData();
	bool CheckVis(bool bAllIN, bool bAllowRenderIntoCBuffer, const Vec3 & vSegmentOrigin);
	void SetupTexturing(bool bMakeUncompressedForEditing = false);
  void RequestTextures();
	void SetSectorTexture(unsigned int textureId);
	void CheckNodeGeomUnload();
	IRenderMesh * MakeSubAreaRenderMesh(const Vec3 & vPos, float fRadius, IRenderMesh * pPrevRenderMesh, IMaterial * pMaterial, bool bRecalIRenderMeshconst, const char * szLSourceName);
	void SetChildsLod(int nNewGeomLOD);
  int GetAreaLOD();
	bool RenderNodeHeightmap();
	bool CheckUpdateProcObjects();
	void IntersectTerrainAABB(const AABB & aabbBox, PodArray<CTerrainNode*> & lstResult);
	void UpdateDetailLayersInfo(bool bRecursive);
	void RemoveProcObjects(bool bRecursive = false);
	void IntersectWithShadowFrustum(bool bAllIn, PodArray<CTerrainNode*> * plstResult, ShadowMapFrustum * pFrustum, const float fHalfGSMBoxSize);
	void IntersectWithBox(const AABB & aabbBox, PodArray<CTerrainNode*> * plstResult);
  void GetTerrainAOTextureNodesInBox(const AABB & aabbBox, PodArray<CTerrainNode*> * plstResult);
	CTerrainNode * FindMinNodeContainingBox(const AABB & aabbBox);
	bool RenderSector(); // returns true only if the sector rendermesh is valid and does not need to be updated
	CTerrainNode * GetTexuringSourceNode(int nTexMML, eTexureType eTexType);
	CTerrainNode * GetReadyTexSourceNode(int nTexMML, eTexureType eTexType);
	int GetData(byte * & pData, int & nDataSize, EEndian eEndian, SHotUpdateInfo * pExportInfo);


	void RescaleToInt();
  template<class T> 
  int Load_T(T * & f, int & nDataSize, EEndian eEndian, bool bSectorPalettes, SHotUpdateInfo * pExportInfo);
  int Load(uint8 * & f, int & nDataSize, EEndian eEndian, bool bSectorPalettes, SHotUpdateInfo * pExportInfo);
  int Load(FILE * & f, int & nDataSize, EEndian eEndian, bool bSectorPalettes, SHotUpdateInfo * pExportInfo);
  int ReloadModifiedHMData(FILE * f);
  void ReleaseHoleNodes();

	void UnloadNodeTexture(bool bRecursive);
	float GetSurfaceTypeAmount(Vec3 vPos, int nSurfType);
	void GetMemoryUsage(ICrySizer*pSizer) const;
	void GetResourceMemoryUsage(ICrySizer*pSizer,const AABB& cstAABB);
//	void GetProcVegetMemoryUsage(ICrySizer*pSizer);

	void SetLOD();
	uint8 GetTextureLOD(float fDistance);

//	void SetHeightmapAABBAndHoleFlag();

	void ReleaseHeightMapGeometry(bool bRecursive = false, const AABB * pBox = NULL);
	void ResetHeightMapGeometry(bool bRecursive = false, const AABB * pBox = NULL);
	int GetSecIndex();

	void DrawArray();
	void UpdateRenderMesh(struct CStripsInfo * pArrayInfo, bool bUpdateVertices);
	void BuildVertices(int step, bool bSafetyBorder);  
	
	int  GetMML(int dist, int mmMin, int mmMax);

	PodArray<CDLight*> * GetAffectingLights(int nEngineFrameID);
	void AddLightSource(CDLight * pSource, int nEngineFrameID);
  void CheckInitAffectingLights(int nEngineFrameID);

//	void GenerateIndicesForQuad(IRenderMesh * pRM, Vec3 vBoxMin, Vec3 vBoxMax, PodArray<uint16> & dstIndices);

	uint32 GetLastTimeUsed() { return m_nLastTimeUsed; }

	void AddIndexAliased(int _x, int _y, int _step,  int nSectorSize, PodArray<CTerrainNode*> * plstNeighbourSectors, CStripsInfo * pArrayInfo);
	static void GenerateIndicesForAllSurfaces(IRenderMesh * pRM, bool bOnlyBorder, short arrpNonBorderIdxNum[MAX_SURFACE_TYPES_COUNT][4], int nBorderStartIndex, SSurfaceTypeInfo * pSurfaceTypeInfos, int nSID, CUpdateTerrainTempData *pUpdateTerrainTempData = NULL);
	void BuildIndices(CStripsInfo & si, PodArray<CTerrainNode*> * pNeighbourSectors, bool bSafetyBorder);
	void BuildIndicesSubdivided(const TRectI &rc, int nStep, int nSectorSize, PodArray<CTerrainNode*> * pNeighbourSectors, CStripsInfo & si);
	
	// entry and sync functions for SPU asynchrony RenderSectorUpdate
	void BuildIndices_Wrapper();
	void BuildVertices_Wrapper();  
	void RenderSectorUpdate_Finish();

	static void UpdateSurfaceRenderMeshes(IRenderMesh * pSrcRM, struct SSurfaceType * pSurface, IRenderMesh * & pMatRM, int nProjectionId, PodArray<unsigned short> & lstIndices, const char * szComment, bool bUpdateOnlyBorders, int nNonBorderIndicesCount);
	void SetupTexGens();
	static void SetupTexGenParams(SSurfaceType * pLayer, float * pOutParams, uint8 ucProjAxis, bool bOutdoor);

	int CreateSectorTexturesFromBuffer();
		
	bool CheckUpdateDiffuseMap();
	bool AssignTextureFileOffset(int16 * &pIndices, int16 & nElementsNum);
	static CProcVegetPoolMan * GetProcObjPoolMan() { return m_pProcObjPoolMan; }
	static SProcObjChunkPool * GetProcObjChunkPool() { return m_pProcObjChunkPool; }
	static void SetProcObjPoolMan(CProcVegetPoolMan * pProcObjPoolMan) { m_pProcObjPoolMan = pProcObjPoolMan; }
	static void SetProcObjChunkPool(SProcObjChunkPool * pProcObjChunkPool) { m_pProcObjChunkPool = pProcObjChunkPool; }
	void UpdateDistance();
	const float GetDistance();
	bool IsProcObjectsReady() { return m_bProcObjectsReady!=0; }
  void UpdateRangeInfoShift();
  
	int GetSectorSizeInHeightmapUnits() const;

	void OffsetPosition(const Vec3& delta);

	void CheckLeafData()
	{ 
		if(!m_pLeafData)
			m_pLeafData = new STerrainNodeLeafData; 
	}

	inline STerrainNodeLeafData * GetLeafData() { return m_pLeafData; }

	//////////////////////////////////////////////////////////////////////////
	// Member variables
	//////////////////////////////////////////////////////////////////////////
public:
	IReadStreamPtr m_pReadStream;
	EFileStreamingStatus m_eTexStreamingStatus;

  CTerrainNode * m_pChilds; // 4 childs or NULL

	// flags
	uint8 m_bProcObjectsReady : 1;
	uint8 m_bMergeNotAllowed : 1;
	uint8 m_bHasHoles : 2; // sector has holes in the ground
	uint8 m_bHasLinkedVoxel: 1; // for editor
	uint8 m_bNoOcclusion : 1; // sector has visareas under terrain surface
  uint8 m_bUpdateOnlyBorders : 1; // remember if only the border were updated
	uint8 // LOD's
		m_cNewGeomMML, m_cCurrGeomMML, 
		m_cNewGeomMML_Min, m_cNewGeomMML_Max,
		m_cNodeNewTexMML, m_cNodeNewTexMML_Min; 
  uint8 m_nTreeLevel;

	uint32 m_nEditorDiffuseTex; // for editor

  uint16 m_nOriginX, m_nOriginY; // sector origin
  int m_nLastTimeUsed; // basically last time rendered
  int m_nSetLodFrameId;

  float * m_pGeomErrors; // errors for each lod level

protected:

	// temp data for terrain generation
	CUpdateTerrainTempData *m_pUpdateTerrainTempData;

public:

  PodArray<CDLight*> m_lstAffectingLights; uint32 m_nLightMaskFrameId;

	PodArray<SSurfaceTypeInfo> m_lstSurfaceTypeInfo;

	SRangeInfo m_rangeInfo;

	STerrainNodeLeafData* m_pLeafData;

	CProcObjSector * m_pProcObjPoolPtr;

  SSectorTextureSet m_nNodeTexSet, m_nTexSet; // texture id's

  uint16 m_nNodeTextureLastUsedSec4;
  uint16 m_nSID;

	AABB m_boxHeigtmapLocal;
	struct CTerrainNode * m_pParent;
  int m_nGSMFrameId;

	float m_arrfDistance[MAX_RECURSION_LEVELS];
	int m_nNodeTextureOffset;
  int m_nNodeHMDataOffset;
  int FTell(uint8 * & f);
  int FTell(FILE * & f);

	static PodArray<uint16> m_arrIndices[MAX_SURFACE_TYPES_COUNT][4];
	static PodArray<SSurfaceType *> m_lstReadyTypes;
	static CProcVegetPoolMan * m_pProcObjPoolMan;
	static SProcObjChunkPool * m_pProcObjChunkPool;
  static int m_nNodesCounter;

  struct CRNTmpData * m_pRNTmpData;
};

// Container to manager temp memory as well as running update jobs
class CTerrainUpdateDispatcher : public Cry3DEngineBase
{
public:
	CTerrainUpdateDispatcher();
	~CTerrainUpdateDispatcher();

	void QueueJob(CTerrainNode*);
	void SyncAllJobs();
  bool Contains(CTerrainNode* pNode) 
  { return (m_queuedJobs.Find(pNode) != -1 || m_arrRunningJobs.Find(pNode) != -1); }; 

	void GetMemoryUsage(ICrySizer *pSizer) const;

	void RemoveJob (CTerrainNode* pNode);

private:
	bool AddJob(CTerrainNode*, bool executeOnSpu );

  static const size_t TempPoolSize = 



    (4U << 20);


  typedef CFirstFit<CInPlace<CMemoryDynamic>,CListItemInPlace> TTemporaryPool;
  TTemporaryPool m_TempStorage;

	PodArray<CTerrainNode*>		m_queuedJobs;
	PodArray<CTerrainNode*>		m_arrRunningJobs;
};

#pragma pack(push,4)

struct STerrainNodeChunk
{
	int16	nChunkVersion;
  int16 bHasHoles;
	AABB	boxHeightmap;
	float fOffset;
	float fRange;
	int		nSize;
	int		nSurfaceTypesNum;

	AUTO_STRUCT_INFO
};

#pragma pack(pop)

#endif
